import 'dart:async';

import 'package:flutter/material.dart';

class Marquee extends StatefulWidget {
  final double height;

  final String text;
  final TextStyle textStyle;
  final int timeDelay;
  final double offsetStep;

  const Marquee({
    Key key,
    this.height,
    this.text,
    this.textStyle,
    this.timeDelay = 100,
    this.offsetStep = 5.0,
  }) : super(key: key);
  @override
  _MarqueeState createState() => _MarqueeState();
}

class _MarqueeState extends State<Marquee> {
  Timer timer;
  @override
  void initState() {
    super.initState();
    timer = Timer.periodic(Duration(milliseconds: widget.timeDelay), (timer) {
      current -= widget.offsetStep;
      setState(() {});
    });
  }

  var current = 0.0;

  @override
  void dispose() {
    timer.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return NotificationListener<_RefreshNotification>(
      onNotification: _notification,
      child: ClipRect(
        child: Builder(
          builder: (ctx) => CustomPaint(
                painter: _MarqueePainter(
                  text: widget.text,
                  style: widget.textStyle ?? DefaultTextStyle.of(context).style,
                  current: current,
                  context: ctx,
                ),
                child: Container(
                  height: widget.height,
                ),
              ),
        ),
      ),
    );
  }

  bool _notification(_RefreshNotification notification) {
    this.current = notification.max;
    return false;
  }
}

class _MarqueePainter extends CustomPainter {
  final String text;
  TextStyle style;
  BuildContext context;

  double max = 0.0;
  double current = 0.0;

  _MarqueePainter({this.text = "", this.style, this.current, this.context});

  @override
  void paint(Canvas canvas, Size size) {
    var painter = TextPainter(
      text: TextSpan(
        text: text,
        style: style,
      ),
      textDirection: TextDirection.ltr,
      textAlign: TextAlign.start,
    );

    painter.layout();
    max = painter.size.width;
    var needScroll = painter.size.width > size.width;
    if (needScroll) {
      painter.paint(canvas, Offset(current, 0.0));
      if (max < current.abs()) {
        print('dispatch notify');
        print("current = ${current.abs()}");
        var notification = _RefreshNotification()..max = size.width;
        notification.dispatch(context);
      }
    } else {
      painter.paint(canvas, Offset.zero);
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}

class _RefreshNotification extends Notification {
  double max = 0.0;
}
